package cn.t.facade.android.engine.cabinet;


import cn.t.constants.CommonConstants;
import cn.t.constants.ParamConstants;
import cn.t.constants.ResponseError;
import cn.t.core.exception.BizException;
import cn.t.dto.device.CabinetHandlingOCDTO;
import cn.t.dto.device.CargowaySkuDTO;
import cn.t.dto.device.DeviceOpenRecordDTO;
import cn.t.dto.device.vo.OpenCloseDTO;
import cn.t.dto.device.vo.QtyVO;
import cn.t.dto.device.vo.WeightVO;
import cn.t.dto.order.ReplenishmentDTO;
import cn.t.dto.order.ReplenishmentDetailDTO;
import cn.t.dto.order.android.OverPickupDetailDTO;
import cn.t.dto.order.android.PickupDetailDTO;
import cn.t.dto.order.base.VBaseDTO;
import cn.t.dto.order.repenlement.ReplenishmentPageDTO;
import cn.t.enums.ActionTypeEnum;
import cn.t.enums.OptionTypeEnum;
import cn.t.enums.device.DeviceTypeEnum;
import cn.t.enums.order.ErrorEnum;
import cn.t.enums.order.ErrorTypeEnum;
import cn.t.enums.order.MsvmOrderStatusEnum;
import cn.t.enums.order.WorkStatusEnum;
import cn.t.model.device.DeviceDataDO;
import cn.t.model.order.ExceptionLogDO;
import cn.t.model.order.OrderDO;
import cn.t.model.order.OrderDetailDO;
import cn.t.model.order.OverPickNumDO;
import cn.t.model.order.WorkOrderDO;
import cn.t.utils.DateUtil;
import cn.t.utils.MsvmUtil;
import cn.t.vo.device.DeviceBaseInfoVO;
import cn.t.vo.user.UserSessionVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toMap;

/**
 * @author l
 */
@Slf4j
@Component
public class RotatingCabinetStrategy extends GenericCabinetHandlingHandler {


    @Override
    public PickupDetailDTO orderClose(CabinetHandlingOCDTO model, OptionTypeEnum optionTypeEnum, UserSessionVO sessionVO) throws ExecutionException, InterruptedException {
        if (ObjectUtils.isEmpty(model.getOrderSn())) {
            throw new BizException(ErrorEnum.ORDER_SN_MISSING, "订单号缺失,无法下单！");
        }
        //订单信息查询
        OrderDO orderDO = orderFacade.getOrderDO(model.getOrderSn());
        UserSessionVO session = userSessionFacade.getSession();
        //  参数校验
        OpenCloseDTO openCloseWeightVO = supplementaryGoodsCheck(model, session, OptionTypeEnum.order_open, ActionTypeEnum.close);
        //开关门数据记录
        updateSingleDeviceData(openCloseWeightVO);
        //判断工单状态，如果是最后完成状态手动设置订单状态
        WorkOrderDO workOrder = workOrderFacade.getWorkOrder(orderDO.getWorkOrderSn());
        if (workOrder != null) {
            if (WorkStatusEnum.COMPLETED.getCode().equals(workOrder.getStatus())) {
                orderDO.setOrderStatus(MsvmOrderStatusEnum.NORMAL.getCode());
                orderDO.setGmtClose(DateUtil.dateByStr(openCloseWeightVO.getRecordAt()));
            }
        }
        return new PickupDetailDTO();
    }

    @Override
    public String replenishmentGoodsOpen(CabinetHandlingOCDTO model) {
        return null;
    }

    @Override
    public void weightCount(OpenCloseDTO model) {
        Map<Integer, CargowaySkuDTO> cargowaySkuMap = model.getCargowaySkuMap();
        List<QtyVO> qtyList = model.getScale().stream().map(item -> new QtyVO(item.getNo(), item.getNum(), (double) 0L)).collect(Collectors.toList());
        qtyList = qtyList.stream().map(item -> {
            Integer qty = item.getQty();
            if (qty != null && qty > 0) {
                CargowaySkuDTO cargowaySkuDTO = cargowaySkuMap.get(item.getNo());
                item.setSkuCode(cargowaySkuDTO.getSkuCode());
                return item;
            } else {
                log.warn("请输入有效的领用数量  货道号为： {}", item.getNo());
                return null;
            }
        }).filter(Objects::nonNull).collect(Collectors.toList());
        model.setOpenScale(model.getScale().stream().collect(Collectors.toMap(WeightVO::getNo, weightVO -> weightVO.getWeight() == null ? 0 : weightVO.getWeight())));
        model.setScaleMap(model.getScale().stream().collect(Collectors.toMap(WeightVO::getNo, weightVO -> weightVO.getWeight() == null ? 0 : weightVO.getWeight())));
        model.setQtyList(qtyList);
    }

    @Override
    public void addException(OpenCloseDTO model) {
        //货道数据结果处理不为空
        if (org.apache.commons.lang3.ObjectUtils.isNotEmpty(model.getQtyList())) {
            List<QtyVO> errQtyList = model.getQtyList().stream().filter(qtyVO -> qtyVO.getErrorTypeEnum() != null).collect(Collectors.toList());
            List<ExceptionLogDO> errList = errQtyList.stream().map
                    (qtyErr -> exceptionLogDTOConverter.getExceptionLogDO(model, model.getCargowaySkuMap().get(qtyErr.getNo()),
                            qtyErr)).collect(Collectors.toList());
            if (!org.springframework.util.ObjectUtils.isEmpty(errList)) {
                log.info("异常记录条数量：订单号{} {}", model.getOrderSn(), errList.size());
                exceptionLogService.saveBatch(errList);
            }
        } else {
            //未补货
            if (OptionTypeEnum.rs_open.equals(model.getOptionTypeEnum())) {
                exceptionLogFacade.exceptionAdd(model, ErrorTypeEnum.ERROR24);
            } else if (OptionTypeEnum.order_open.equals(model.getOptionTypeEnum())) {
                //未领取
                exceptionLogFacade.exceptionAdd(model, ErrorTypeEnum.ERROR10);
            }

        }

    }

    @Override
    public void replenishmentErrLog(Map<Integer, ReplenishmentDetailDTO> detailMap, OpenCloseDTO openCloseDO) {
        for (QtyVO qtyVO : openCloseDO.getQtyList()) {
            CargowaySkuDTO cargowaySkuDTO = openCloseDO.getCargowaySkuMap().get(qtyVO.getNo());
            ReplenishmentDetailDTO replenishmentDetailDTO = detailMap.get(qtyVO.getNo());

            ErrorTypeEnum errorTypeEnum = checkErr(cargowaySkuDTO, qtyVO, replenishmentDetailDTO);
            qtyVO.setErrorTypeEnum(errorTypeEnum);
        }
    }

    @Override
    public List<PickupDetailDTO.DetailVO> responsePickup(OpenCloseDTO openCloseDO) {
        return null;
    }

    public ErrorTypeEnum checkErr(CargowaySkuDTO cargowaySkuDTO, QtyVO qtyVO, ReplenishmentDetailDTO detail) {
        ErrorTypeEnum validate = validate(cargowaySkuDTO, qtyVO);
        if (validate != null) {
            return validate;
        }
        if (ObjectUtils.isEmpty(detail) && qtyVO.getQty() > 0) {
            //非计划补货货道增重
            return ErrorTypeEnum.ERROR28;
        }
        return null;
    }

    @Override
    public List<OverPickupDetailDTO> responseOverPickup(OverPickNumDO overPickNumDO, List<PickupDetailDTO.DetailVO> detailVOS) {
        return null;
    }

    @Override
    public void pickErrLog(OpenCloseDTO openCloseDO) {
        Map<Integer, CargowaySkuDTO> cargowaySkuMap = openCloseDO.getCargowaySkuMap();
        //根据类型异常判断
        for (QtyVO qtyVO : openCloseDO.getQtyList()) {
            ErrorTypeEnum errorTypeEnum = pickCheckErr(cargowaySkuMap.get(qtyVO.getNo()), qtyVO);
            qtyVO.setErrorTypeEnum(errorTypeEnum);
        }
    }

    public ErrorTypeEnum pickCheckErr(CargowaySkuDTO cargowaySkuDTO, QtyVO qtyVO) {
        ErrorTypeEnum validate = validate(cargowaySkuDTO, qtyVO);
        if (validate != null) {
            return validate;
        }
        Integer qty = qtyVO.getQty();
        if (cargowaySkuDTO.getInventory() <= 0 && qty > 0) {
            //零库存领取
            return ErrorTypeEnum.ERROR9;
        }
        if (qty == -1) {
            //超出误差范围
            return ErrorTypeEnum.ERROR6;
        }
        return null;
    }

    /**
     * 计算前验证
     */
    public ErrorTypeEnum validate(CargowaySkuDTO cargowaySkuDTO, QtyVO qtyVO) {
        //正确结果为正整数和0
        if (cargowaySkuDTO == null) {
            //货道未上架商品
            return ErrorTypeEnum.ERROR22;
        }
        if (cargowaySkuDTO.getSkuWeight() == null || cargowaySkuDTO.getSkuWeight().compareTo(BigDecimal.ZERO) == 0) {
            return ErrorTypeEnum.ERROR8;
        }
        if (ObjectUtils.isEmpty(qtyVO.getQty()) || ObjectUtils.isEmpty(qtyVO.getWeight())) {
            //货道数据错乱不匹配
            //重力数量未获取到
            return ErrorTypeEnum.ERROR23;
        }

        //货道硬件损坏
        if (qtyVO.getQty() <= 0) {
            //货道硬件损坏
            return ErrorTypeEnum.ERROR4;
        }


        return null;
    }

    @Override
    public Boolean replenishmentGoodsClose(CabinetHandlingOCDTO model) throws ExecutionException, InterruptedException {
        UserSessionVO session = userSessionFacade.getSession();

        //  参数校验
        OpenCloseDTO openCloseWeightVO = goodsCheck(model, session, OptionTypeEnum.rs_open, ActionTypeEnum.close);
        openCloseWeightVO.setRequestId(openCloseWeightVO.getRequestId());
        //关门数据记录
        closeDeviceData(openCloseWeightVO);
        //重力与数量的转换
        weightCount(openCloseWeightVO);
        //补货单异常记录
        if (openCloseWeightVO.getScale() == null) {
            exceptionLogFacade.exceptionAdd(openCloseWeightVO, ErrorTypeEnum.ERROR26);
            return true;
        }
        //疑似未补货
        if (openCloseWeightVO.getQtyList().size() == 0) {
            //  异常记录
            addException(openCloseWeightVO);
            return true;
        }

        //补货
        VBaseDTO<ReplenishmentPageDTO, ReplenishmentDetailDTO> replenishmentVO = replenishmentDetailDTOMap(openCloseWeightVO.getOrderSn());
        Map<Integer, ReplenishmentDetailDTO> replenishmentDetailMap = replenishmentVO.getDetail().stream().collect(Collectors.toMap(ReplenishmentDetailDTO::getCargowayNo, Function.identity()));


        //行异常记录
        replenishmentErrLog(replenishmentDetailMap, openCloseWeightVO);
        addException(openCloseWeightVO);

            //补货状态和库存记录

        log.info("补货starttime {}",System.currentTimeMillis());
            replenishmentFacade.updateStatus(replenishmentVO, openCloseWeightVO);
        log.info("补货end {}",System.currentTimeMillis());
        CompletableFuture.runAsync(() -> {
            //更新货道数据 (重量和库存)
            cargowaySkuFacade.updateByOpenCloseData(openCloseWeightVO);
        }, executeIoExecutor);
        return true;
    }

    private void closeDeviceData(OpenCloseDTO openCloseWeightVO) {
        //开门记录
        deviceOpenRecordFacade.openCloseAddInfo(openCloseWeightVO);
        //设备数据
        deviceDataFacade.addInfo(openCloseWeightVO);
    }

    @Override
    public OpenCloseDTO openDoor(CabinetHandlingOCDTO model, OptionTypeEnum optionTypeEnum) throws ExecutionException, InterruptedException {
        if (ObjectUtils.isEmpty(model.getOrderSn())) {
            throw new BizException(ErrorEnum.ORDER_SN_MISSING, "订单号缺失,无法下单！");
        }

        UserSessionVO session = userSessionFacade.getSession();
        //  参数校验
        OpenCloseDTO openCloseWeightVO = supplementaryGoodsCheck(model, session, OptionTypeEnum.order_open, ActionTypeEnum.open);
        //订单信息查询
        OrderDO orderDO = orderFacade.getOrderDO(model.getOrderSn());
        //数据处理
        weightCount(openCloseWeightVO);
        //异常判断，设置领用行异常
        pickErrLog(openCloseWeightVO);
        //  异常记录
        addException(openCloseWeightVO);
        List<OrderDetailDO> orderDetailList = orderDetailDTOConverter.toOderDetailList(openCloseWeightVO);
        //判断是否超领（工单 商品）
        OverPickNumDO overPickNumDO = overPickupRecordFacade.checkOverPickupAndUpdate(orderDO, orderDetailList, Boolean.TRUE);
        //新增订单明细
        if (!org.springframework.util.ObjectUtils.isEmpty(orderDetailList)) {
            orderDetailService.saveOrUpdateBatch(orderDetailList);
        }
        String requestId = MsvmUtil.getOrderSn(model.getDeviceId());
        openCloseWeightVO.setRequestId(requestId);
        //超领更新 和 工单状态更新
        overPickupRecordFacade.addOverPick(overPickNumDO, orderDO);
        //开关门数据记录
        addSingleDeviceData(openCloseWeightVO);
        openCloseWeightVO.setRequestId(requestId);
        if (orderDO.getOrderStatus() == null) {
            //异常
            boolean isErr = openCloseWeightVO.getQtyList().stream().anyMatch(qtyVO -> qtyVO.getErrorTypeEnum() != null) || openCloseWeightVO.getErrorTypeEnum() != null;
            if (isErr || org.springframework.util.ObjectUtils.isEmpty(openCloseWeightVO.getQtyList())) {
                orderDO.setOrderStatus(MsvmOrderStatusEnum.ERR_ORDER.getCode());
            } else {
                orderDO.setOrderStatus(MsvmOrderStatusEnum.NORMAL.getCode());
            }
        }
        setOrderTime(orderDO);
        orderFacade.update(orderDTOConverter.model2Dto(orderDO));

        return openCloseWeightVO;
    }

    //设置开关门时间
    private void setOrderTime(OrderDO orderDO) {
        List<DeviceOpenRecordDTO> deviceOpenRecordList = deviceOpenRecordFacade.getDeviceOpenRecordList(Collections.singletonList(orderDO.getOrderSn()));
        DeviceOpenRecordDTO end = deviceOpenRecordList.stream().filter(deviceOpenRecordDTO -> deviceOpenRecordDTO.getBusinessType().equals(OptionTypeEnum.order_open.getCode())
                && deviceOpenRecordDTO.getGmtOpen() != null).max(Comparator.comparing(DeviceOpenRecordDTO::getGmtOpen)).orElse(null);
        if (end != null) {
            orderDO.setGmtClose(end.getGmtOpen());
        }
        DeviceOpenRecordDTO first = deviceOpenRecordList.stream().filter(deviceOpenRecordDTO -> deviceOpenRecordDTO.getBusinessType().equals(OptionTypeEnum.order_open.getCode())
                && deviceOpenRecordDTO.getGmtOpen() != null).min(Comparator.comparing(DeviceOpenRecordDTO::getGmtOpen)).orElse(null);
        if (first != null) {
            orderDO.setGmtOpen(first.getGmtOpen());
        }
    }

    public void addSingleDeviceData(OpenCloseDTO openCloseWeightVO) {
        //关门数据
        Map<Integer, WeightVO> weightMap = openCloseWeightVO.getScale().stream().collect(toMap(WeightVO::getNo, Function.identity()));
        //货道商品信息
        Map<Integer, CargowaySkuDTO> cargowaySkuMap = openCloseWeightVO.getCargowaySkuMap();
        List<CargowaySkuDTO> cargowaySkuDTOList = openCloseWeightVO.getQtyList().stream().map(qtyVO -> {
            CargowaySkuDTO cargowaySkuDTO = cargowaySkuMap.get(qtyVO.getNo());
            WeightVO weightVO = weightMap.get(qtyVO.getNo());
            if (!ObjectUtils.isEmpty(cargowaySkuDTO)) {
                //货道重力库存更新
                cargowaySkuDTO.setWeight(weightVO.getWeight());
                int inventory = cargowaySkuDTO.getInventory() != null ? cargowaySkuDTO.getInventory() : 0;
                cargowaySkuDTO.setInventory(Math.max(qtyVO.getQty() * -1 + inventory, 0));
                return cargowaySkuDTO;
            } else {
                log.warn("货道不存在" + cargowaySkuDTO.getCargowayNo());
                return null;
            }

        }).filter(Objects::nonNull).collect(Collectors.toList());
        //开门记录
        deviceOpenRecordFacade.addInfo(openCloseWeightVO);
        cargowaySkuFacade.batchUpdate(cargowaySkuDTOList);


    }

    @Override
    public String getDeviceProcessType() {
        return ParamConstants.CABINET_TYPE + DeviceTypeEnum.ROTATING_CABINET.getCode();
    }


    public OpenCloseDTO goodsCheck(CabinetHandlingOCDTO model, UserSessionVO session, OptionTypeEnum optionTypeEnum, ActionTypeEnum actionTypeEnum) throws ExecutionException, InterruptedException {
        if (CollectionUtils.isEmpty(model.getScale())) {
            log.warn("货道数据为空,请联系管理员 {}", model.getOrderSn());
            return null;
        }
        OpenCloseDTO openCloseWeightVO = converter.dto2VO(model);
        openCloseWeightVO.setActionTypeEnum(actionTypeEnum);
        //用户信息检查
        check(session);


        //校验重力数据
        List<DeviceDataDO> dataByOrderSn = deviceDataFacade.findDataByOrderSn(openCloseWeightVO, optionTypeEnum, DateUtil.dateByStr(model.getRecordAt()));
        if (!ObjectUtils.isEmpty(dataByOrderSn)) {
            throw new BizException(ResponseError.DATA_ERR, "重复处理" + dataByOrderSn.get(0).getOrderSn());
        }
        //补货信息
        ReplenishmentDTO replenishmentDO = new ReplenishmentDTO(model.getOrderSn().trim(), model.getDeviceId());
        List<ReplenishmentDTO> repositoryList = replenishmentFacade.queryList(replenishmentDO, CommonConstants.ZERO, Integer.MAX_VALUE, null, null);
        if (CollectionUtils.isEmpty(repositoryList)) {
            throw new BizException(ResponseError.RS_DATA_NOTFOUND_ERR, ResponseError.RS_DATA_NOTFOUND_ERR.getName());
        }

        //设备检查
        CompletableFuture<DeviceBaseInfoVO> deviceCompletableFuture = CompletableFuture.supplyAsync(() -> deviceFacade.deviceBaseInfo(model.getDeviceId()), executeIoExecutor);

        //货道库存检查
        CompletableFuture<Map<Integer, CargowaySkuDTO>> cargowaySkuMapCompletableFuture = CompletableFuture.supplyAsync(() -> cargowaySkuFacade.cargowayMap(model.getDeviceId()), executeIoExecutor);
        DeviceBaseInfoVO device = deviceCompletableFuture.get();

        //set data
        converter.setCloseOpenData(openCloseWeightVO, device, session);
        //货道sku信息
        openCloseWeightVO.setCargowaySkuMap(cargowaySkuMapCompletableFuture.get());

        //操作类型
        openCloseWeightVO.setOptionTypeEnum(optionTypeEnum);
        return openCloseWeightVO;
    }

}
